<script lang="ts" setup>
import { inject, reactive,watch } from "vue";

// 定义接口
interface PropsType {
  title?: String;
  count?:any
  [key: string]: any;
}

const obj = reactive({ count: 0 });
//-------------------------------------------------------- 组件传参（在这里是子组件）
/**
 * 任何类型的值都可以作为 props 的值被传递。所有的 props 都遵循着单向绑定原则，也就是说父组件
 * 传递的值被修改了 他会更新 所有的子组件 而子组件的值变动了 他不会去更新引用它的父组件
 * */

// 父传子 - 自定义属性
// 1. 生命接受的 属性
// const { title } = defineProps(["title"]);

// 2. 以对象的形式接收 并设置属性的类型  默认值及必传！！！！
// const { title } = defineProps({
//   title: {
//     type: String, // 类型
//     required: true, // 是否必传
//     defatule: "当不传递props时 就使用我", // 默认值！！！
//   },
// });

// 3. 以ts传递类型参数的形式
const { title ,count} = defineProps<PropsType>(); // 定义自定义属性

console.log("contcontcontcont",count);



// -----  子传父 -自定义事件  ( 与 react 不同 )
const emit = defineEmits(["setBooks"]); // 定义自定义事件 设置ts类型 选项 跟自定义属性是一样的
/** 通过$emit 来触发绑定在自身组件上的自定义事件 $emit("自身上的自定义事件名",参数...)
 *  $emit 方法可直接在 <template>标签中使用如果想要在 <script setup> 使用则需要跟定义
 *  props一样 使用 defineEmits 方法定义一下 自定义事件名
 *  使用时 emit("事件名",参数)
 *
 */

// ------------------------------------------------ 组件上的 v-model

/**
 * - Father.vue-
 *  const num = ref(0)
 *  <A  v-model={num}  />
 *
 * /- 如果在组件上使用v-model的话，vue会自动转换成...下面的格式 请记住这句话，转换时自定义事件 自定义属性时固定的 你获取的时候液只能通过它所转换的名称获取 -/
 *
 *  <A
 *  :modelValue="num"
 *  @update:modelValue="num => num = num"
 *  />
 *
 * - Son.vue -
 * /- 在son中 我们就可以通过 defineEmits 和 defineProps 定义来访问这两个值 -/
 *
 *  const props = defineProps(['modelValue'])
 *  const emit = defineEmits(['update:modelValue'])
 *
 *  如果不喜欢v-model 默认解析出来的 modelValue ，或者定义多个 v-model时 可写成 <A  v-model:自定义={num}  /> 的形式 ，获取时页也要发生变化  defineEmits(['update:自定义'])
 *  这个update是不能改变的。
 *
 *  当然组件上的v-model也可以添加修饰符 <A v-model.trim={num} /> 。 还可以自定义修饰符...详情官网
 */

// ------------------------------------------------------------- 组件属性透传
// 透传指的是传递给一个组件的属性，却没有被该组件声明为 props 或 emits ， 那么这些值将自动绑定在组件的根元素上
/**
 *  -  Father.vue -
 *  <A class="title"/>
 *
 *
 *  - A.vue -
 *  <template>
 *      <div class="title"> </div>
 *  </template>
 *
 * -- 写在A组件上的属性（事件） 如果A组件没有申明为props或emits时，他会自动透传到A组件的元素身上
 *    如果 子组件的根元素已存在class style ，vue会将这些class style 进行合并。 如果 组件上存在 和 根元素相同的事件 那么当触发时这两个事件都会触发
 *    如果 根元素是一个组件的话 那么这些属性会依次嫡传下去，前提没有被props emits 声明过
 * --
 *
 * -- 指定透传属性挂载 $attrs --
 * - $attrs 对象包含了除组件所声明的 props 和 emits 之外的所有其他 attribute，
 * - 我们可以通过v-bind 来指定某一个dom挂栽 透传的属性
 *   <div class="btn-wrapper">
 *     <button class="btn" v-bind="$attrs">click me</button>
 *   </div>
 *
 *
 *  -- 禁用透传属性 --
 * 如果你使用了 <script setup>，你需要一个额外的 <script> 块来书写这个选项声明
 *  《script》
 *    // 使用普通的 <script> 来声明选项
 *    export default {
 *     inheritAttrs: false
 *    }
 *  《/script》
 */

function onClick() {
  emit("setBooks");
}

// ---------------------------------------------临时面试题-----------------------------------
// 地址引用 地址嵌套 最里层改变面所有更新
// let arr = [
//   { id: 1, name: "部门1", pid: 0 },
//   { id: 2, name: "部门2", pid: 1 },
//   { id: 3, name: "部门3", pid: 1 },
//   { id: 4, name: "部门4", pid: 3 },
//   { id: 5, name: "部门5", pid: 4 },
// ];

// function arrayToTree(items: any) {
//   let result = [] as any; // 存放结果集
//   let itemMap = {} as any; //

//   for (const item of items) {
//     const id = item.id;
//     const pid = item.pid;

//     if (!itemMap[id]) {
//       // id=1 // id=2 // id=3
//       itemMap[id] = {
//         children: [], // {1:{children:[]}}
//       };
//     }

//     itemMap[id] = {
//       // {1:{children:[]}} // {1: {...}, 2:{children:[]}} // {1: {...}, 2:{children:[]}, 3: {children:[]}}
//       ...item, // id: 1, name: '部门1', pid: 0,
//       children: itemMap[id]["children"], // []
//     };
//     // 以上 给每个数组添加children 属性

//     const treeItem = itemMap[id];
//     if (pid === 0) {
//       result.push(treeItem); // id: 1, name: '部门1', pid: 0, children: []
//     } else {
//       itemMap[pid].children.push(treeItem);
//       //  {1: {..., children: {...}}}
//     }
//     console.log(itemMap);
//   }
//   return result;
// }
// console.log(arrayToTree(arr));

// -----------------------------------------------------------------------------------------


//  -------------------------------- --依赖注入
// Inject (注入) 接受其他地方由 Provide 提供的值
// 当使用 Inject 注入的时候，发现没有改诸如名的时候我们可以通过第二个参数设置默认值
const num = inject('num',"num没提供的时候我就是默认值")

</script>

<template>
  <div>
    <h3>son的内容（接受父组件传递的属性 及 方法 ）------------------------------</h3>
    <h4>props：{{ count}}</h4>
    <h4 v-bind="$attrs">父组件传递过来的值 ： {{ title }}</h4>
    <button @click="$emit('setBooks')">通过调用$emit触发对应的事件</button>
    <button @click="onClick">defineEmits 返回触发 组件上事件</button>
    <p>{{ $attrs }}</p>
    <hr />
    
    <h3> 插槽------------------------------</h3>
    默认插槽--
    <slot sex="男">当没有指定插槽内容时 slot 标签中的就是默认内容</slot>
    <hr />
    <!-- 显示 header 插槽的 内容 -->
    具名header插槽--
    <slot
      name="header"
      :count="44"></slot>
    <hr />
    --依赖注入显示
    <h2>{{num}}</h2>

  </div>
</template>

<style lang="scss"></style>
